#include <cstdio>
#include <iostream>
#include <string>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <SDL/SDL.h>
#include <math.h>
#include "PADSMesh.h"
#include "Image.h"
#include "d_Math.h"

using namespace std;
using namespace d_Math;

GLuint mask;

PADSChart::PADSChart(PADSCam cam, Image *p, Image *n, Image *c, float dx, float dist, int bb_w, int bb_h, int bb_x, int bb_y)
{
	p_size[0]=p_size[1]=0;
	p_size[2]=1/(dist*dist);

	num_splats =0;
	for(int i=0; i<bb_w; i++)
		for(int j=0; j<bb_h; j++)
			if(p->GetR(i,j)==255)
				num_splats++;
	splats = (PADSSplat *) malloc (num_splats*sizeof(PADSSplat));
	int splat_count = 0;

	for(int j=0; j<bb_h; j++)
		for(int i=0; i<bb_w; i++)
			if( p->GetR(i,j) == 255)
			{
				float treshold = (float)p->GetG(i,j)/255.0;
				d_Vec3f r= norm(cam.d*cam.focal + cam.xa*cam.sx*(-1)*(i-cam.cx+bb_x) + cam.ya*cam.sy*(j-cam.cy+bb_y));
				splats[splat_count].vp = cam.vp + r*(treshold*dx+dist);
				splats[splat_count].n = norm(d_Vec3f( (float)n->GetR(i,j)/255.0-0.5,  (float) n->GetG(i,j)/255.0-0.5, (float) n->GetB(i,j)/255.0-0.5));
				splats[splat_count].c = d_Vec3f((float)c->GetR(i,j)/255.0, (float) c->GetG(i,j)/255.0, (float) c->GetB(i,j)/255.0);
				splat_count++;
			}
}

void PADSMesh::drawCameras()
{
	glBegin(GL_LINES);
	for(int i=0; i<num_cams; i++)
	{
		glColor3f(1,1,1);
		glVertex3f(cameras[i].vp[0], cameras[i].vp[1], cameras[i].vp[2]);
		glVertex3f(cameras[i].d[0]*5+cameras[i].vp[0], 
								cameras[i].d[1]*5+cameras[i].vp[1], 
								cameras[i].d[2]*5+cameras[i].vp[2]);
		glVertex3f(cameras[i].vp[0], cameras[i].vp[1], cameras[i].vp[2]);
		glVertex3f(cameras[i].xa[0]*5+cameras[i].vp[0], 
								cameras[i].xa[1]*5+cameras[i].vp[1], 
								cameras[i].xa[2]*5+cameras[i].vp[2]);
		glVertex3f(cameras[i].vp[0], cameras[i].vp[1], cameras[i].vp[2]);
		glVertex3f(cameras[i].ya[0]*5+cameras[i].vp[0], 
								cameras[i].ya[1]*5+cameras[i].vp[1], 
								cameras[i].ya[2]*5+cameras[i].vp[2]);
	}
	glEnd();
}

//typedef void (APIENTRY * GL_PointParameterfvARB_Func)(GLenum, const GLfloat *);
//typedef void (APIENTRY * GL_PointParameterfARB_Func)(GLenum, const GLfloat);
//GL_PointParameterfvARB_Func glPointParameterfvARB_ptr = 0;
//GL_PointParameterfARB_Func glPointParameterfARB_ptr = 0;

void PADSChart::draw()
{
	//glBegin(GL_POINTS);
		for(int i=0; i<num_splats; i++)
		{
			glNormal3f(splats[i].n[0], splats[i].n[1], splats[i].n[2]);
			glColor3f(splats[i].c[0], splats[i].c[1], splats[i].c[2]);
			glVertex3f(splats[i].vp[0], splats[i].vp[1], splats[i].vp[2]);
		}
	//glEnd();
}

PADSMesh::PADSMesh(char *filename)
{
   // glPointParameterfvARB_ptr=(GL_PointParameterfvARB_Func) SDL_GL_GetProcAddress("glPointParameterfvARB");
	//glPointParameterfARB_ptr=(GL_PointParameterfARB_Func) SDL_GL_GetProcAddress("glPointParameterfARB");
	//glPointParameterfARB_ptr(GL_POINT_SIZE_MIN_ARB, 1);
	//glPointParameterfARB_ptr(GL_POINT_SIZE_MAX_ARB, 2000);
	//GLfloat p_size[] = {0,0,1.0/200000.0};
	//glPointParameterfvARB_ptr(GL_POINT_DISTANCE_ATTENUATION_ARB, p_size);
	glPointSize(2);
	glEnable(GL_POINT_SMOOTH);
	glEnable(GL_COLOR_MATERIAL);
	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

	FILE *pads_file = fopen(filename, "r");
	fscanf(pads_file, "PADS Format\n");
	fscanf(pads_file, "patches %i\n", &num_patches);
	look_up = (int *) malloc (num_patches*sizeof(int));
	bb_w = (int *) malloc(num_patches*sizeof(int));
	bb_h = (int *) malloc(num_patches*sizeof(int));
	bb_x = (int *) malloc(num_patches*sizeof(int));
	bb_y = (int *) malloc(num_patches*sizeof(int));
	dist = (float *) malloc(num_patches*sizeof(float));
	dx = (float *) malloc(num_patches*sizeof(float));
	fscanf(pads_file, "cameras %i\n", &num_cams);
	cameras = (PADSCam *) malloc (num_cams*sizeof(PADSCam));
	for(int i=0; i<num_cams; i++)
	{
		float tmp[3];
		fscanf(pads_file, "%f %f %f\n", &tmp[0], &tmp[1], &tmp[2]);
		for(int j=0; j< 3; j++)
			cameras[i].vp[j]=tmp[j];
		fscanf(pads_file, "%f %f %f\n", &tmp[0], &tmp[1], &tmp[2]);
		for(int j=0; j< 3; j++)
			cameras[i].xa[j]=tmp[j];
		fscanf(pads_file, "%f %f %f\n", &tmp[0], &tmp[1], &tmp[2]);
		for(int j=0; j< 3; j++)
			cameras[i].ya[j]=tmp[j];
		fscanf(pads_file, "%f %f %f\n", &tmp[0], &tmp[1], &tmp[2]);
		for(int j=0; j< 3; j++)
			cameras[i].d[j]=tmp[j];
		fscanf(pads_file, "%f %f %f %f %f\n", &cameras[i].sx, &cameras[i].sy, &cameras[i].cx, &cameras[i].cy, &cameras[i].focal);
	}
	for(int i=0; i<num_patches; i++)
	{
		int tmp[5];
		fscanf(pads_file, "%i %i\n", &tmp[0], &tmp[1]);
		look_up[tmp[0]] = tmp[1];
		fscanf(pads_file, "%i %i %i %i\n", &tmp[1], &tmp[2], &tmp[3], &tmp[4]);
		bb_w[tmp[0]] = tmp[1];
		bb_h[tmp[0]] = tmp[2];
		bb_x[tmp[0]] = tmp[3];
		bb_y[tmp[0]] = tmp[4];
		float tmp2[2];
		fscanf(pads_file, "%f %f\n", &tmp2[0], &tmp2[1]);
		dist[tmp[0]] = tmp2[0];
		dx[tmp[0]] = tmp2[1];
	}
	fclose(pads_file);

	p = (Image **) malloc (num_patches * sizeof(Image *));
	for(int i=0; i<num_patches; i++)
	{
		char patch_name[40];
		sprintf(patch_name, "patch_%i_P.gif", i);
		p[i] = new Image(bb_w[i], bb_h[i]);
		p[i]->Load(patch_name);
	}

	n = (Image **) malloc (num_patches * sizeof(Image *));
	for(int i=0; i<num_patches; i++)
	{
		char patch_name[40];
		sprintf(patch_name, "patch_%i_N.gif", i);
		n[i] = new Image(bb_w[i], bb_h[i]);
		n[i]->Load(patch_name);
	}

	c = (Image **) malloc (num_patches * sizeof(Image *));
	for(int i=0; i<num_patches; i++)
	{
		char patch_name[40];
		sprintf(patch_name, "patch_%i_C.gif", i);
		c[i] = new Image(bb_w[i], bb_h[i]);
		c[i]->Load(patch_name);
	}

	/////////////////////////////CHARTS AND SPLATS CREATION/////////////////////////////////
	charts = (PADSChart **) malloc (num_patches * sizeof(PADSChart *));
	for (int k=0; k<num_patches; k++) 
	{
		charts[k] = new PADSChart(cameras[look_up[k]], p[k], n[k], c[k], dx[k], dist[k], bb_w[k], bb_h[k], bb_x[k], bb_y[k]);
	}
}

d_Vec3f PADSChart::get_center()
{
	float xmin=10000, xmax=0;
	float ymin=10000, ymax=0;
	float zmin=10000, zmax=0;
	for(int i=0; i<num_splats; i++)
	{
		if(splats[i].vp[0]<xmin)
			xmin = splats[i].vp[0];
		if(splats[i].vp[0]>xmax)
			xmax = splats[i].vp[0];
		if(splats[i].vp[1]<ymin)
			ymin = splats[i].vp[1];
		if(splats[i].vp[1]>ymax)
			ymax = splats[i].vp[1];
		if(splats[i].vp[2]<zmin)
			zmin = splats[i].vp[2];
		if(splats[i].vp[2]>zmax)
			zmax = splats[i].vp[2];
	}
	return d_Vec3f(xmin,ymin, zmin);
}

d_Vec3f PADSMesh::get_center()
{
	float xmin=10000, xmax=-10000;
	float ymin=10000, ymax=-10000;
	float zmin=10000, zmax=-10000;
	for(int i=0; i<num_patches; i++)
	{
		d_Vec3f aux = charts[i]->get_center();
		if(aux[0]<xmin)
			xmin = aux[0];
		if(aux[0]>xmax)
			xmax = aux[0];
		if(aux[1]<ymin)
			ymin = aux[1];
		if(aux[1]>ymax)
			ymax = aux[1];
		if(aux[2]<zmin)
			zmin = aux[2];
		if(aux[2]>zmax)
			zmax = aux[2];
	}
	return d_Vec3f(xmin+(float)(xmax-xmin)/2.0, ymin + (float)(ymax-ymin)/2.0, zmin+(float)(zmax-zmin)/2.0);
}

typedef unsigned char byte;

void PADSMesh::draw()
{
	glBegin(GL_POINTS);
	for(int k=0; k<num_patches; k++)
		charts[k]->draw();
	glEnd();
}

//////////////Helper funcs for multiresolution
float PADSMesh::get_realbbw(int cindex)
{
	return bb_w[cindex]*cameras[look_up[cindex]].sx;
}

float PADSMesh::get_realbbh(int cindex)
{
	return bb_h[cindex]*cameras[look_up[cindex]].sy;
}

int PADSMesh::get_minchart()
{
	int min;
	float tmp = 100000;
	for(int i=0; i<num_patches; i++)
		if(get_realbbw(i)*get_realbbh(i)<tmp)
		{
			tmp = get_realbbw(i)*get_realbbh(i);
			min = i;
		}
	return min;
}

float PADSMesh::get_targetsize(int index)
{
	if(get_realbbw(index)>get_realbbh(index))
		return get_realbbh(index);
	else
		return get_realbbw(index);
}

int PADSMesh::get_targetsize2(int index)
{
	if(bb_w[index]>bb_h[index])
		return bb_h[index];
	else
		return bb_w[index];
}

int PADSMesh::get_treedepth(int index, float target_size)
{
	int depth=0;
	int w = bb_w[index], h=bb_h[index];//w = get_realbbw(index), h = get_realbbh(index);
	while(w>target_size && h>target_size)
	{
		w = (int)((float)w/2.0);
		h = (int)((float)h/2.0);
		depth++;
	}
	return depth;
}

int PADSMesh::get_top_of_chartw(int index, float size)
{
	int ret=0;
	float w=get_realbbw(index);
	while(w>=size)
	{
		w -= size;
		ret++;
	}
	return ret;
}

int PADSMesh::get_top_of_charth(int index, float size)
{
	int ret=0;
	float h=get_realbbh(index);
	while(h>=size)
	{
		h -= size;
		ret++;
	}
	return ret;
}

int PADSMesh::get_top_of_chartw2(int index, int size)
{
	int ret=0;
	float w=bb_w[index];
	while(w>=size)
	{
		w -= size;
		ret++;
	}
	return ret;
}

int PADSMesh::get_top_of_charth2(int index, int size)
{
	int ret=0;
	float h=bb_h[index];
	while(h>=size)
	{
		h -= size;
		ret++;
	}
	return ret;
}

